package top.riske.imagebed.service;

import cn.hutool.core.util.IdUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import top.riske.imagebed.entity.CFile;
import top.riske.imagebed.exception.FileHadExistException;
import top.riske.imagebed.exception.FileHashDifferentException;
import top.riske.imagebed.mapper.FileMapper;
import top.riske.imagebed.utils.FileUtils;
import top.riske.imagebed.utils.HashUtils;
import top.riske.imagebed.utils.ImageUtils;
import top.riske.imagebed.utils.Result;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;

/**
 * created by vital on 2025/3/5 上午8:41
 */
@Slf4j
@Service
public class FileUploadService {

    @Resource
    private FileMapper fileMapper;

    // 文件保存根路径
    @Value("${imageBed.image.path}")
    private String FILE_ROOT_PATH;

    @Value("${imageBed.server.url}")
    private String imageBedPath;

    /**
     * 预上传文件，检测文件是否存在，存在则返回文件信息
     * @param fileHash
     * @return
     */
    public Result<String> prepareUpload(String fileHash, HttpServletRequest request) {
        // 查询文件是否存在
        CFile cFile = fileMapper.selectByHash(fileHash);
        if(cFile == null){
            return Result.fail("文件不存在");
        }
        // 构建文件链接
        return Result.success(imageBedPath + cFile.getPath());
    }

    /**
     * 校验hash是否一致，如果没有hash，则使用系统生成的hash并返回
     */
    public String checkHash(MultipartFile file, String hash) throws NoSuchAlgorithmException, IOException, FileHashDifferentException {
        // 验证文件hash，全部转为小写
        String actualHash = HashUtils.calculateFileMd5(file).toLowerCase();
        // 判断是否需要校验hash
        if(hash != null && hash.length()== 32){
            hash = hash.toLowerCase();
            if(!actualHash.equals(hash)){
                log.error("文件校验失败，期望的：{}，实际的：{}", hash, actualHash);
                throw new FileHashDifferentException("文件校验失败");
            }
        }else{
            // 没有hash，则使用系统生成的hash
            hash = actualHash;
        }
        return hash;
    }

    /**
     * 保存文件
     */
    public Result<CFile> saveFile(MultipartFile file, String hash, HttpServletRequest request) throws IOException, FileHadExistException {

        // 判断文件是否存在
        Result<String> result = this.prepareUpload(hash, request);
        if(result.isSuccess()){
            throw new FileHadExistException("文件已存在",result.getData());
        }

        // 构建保存文件路径
        String simplePath = createPathByDate();
        // 完整文件路径，相对路径
        String fullPath = FILE_ROOT_PATH + simplePath;
        // 随机的文件名
        String randomFileName = IdUtil.simpleUUID();
        // 保存文件
        CFile cFile = FileUtils.saveFile(file, fullPath, randomFileName,hash);
        cFile.setPath(createDBPath(simplePath,randomFileName,file.getOriginalFilename()));

        // 保存文件信息到数据库
        return saveFileInfoToDB(cFile);
    }

    /**
     * 根据当前时间创建文件路径
     * @return
     */
    private String createPathByDate(){
        LocalDateTime now = LocalDateTime.now();
        return    "/" + now.getYear()
                + "/" + now.getMonthValue()
                + "/" + now.getDayOfMonth();
    }

    private String createDBPath(String simplePath,String fileName,String fileOriginalName){
        return simplePath + "/" + fileName + FileUtils.getFileType(fileOriginalName);
    }

    /**
     * 保存文件信息到数据库
     * @return
     */
    private Result<CFile> saveFileInfoToDB(CFile file){
        try{
            int result = fileMapper.insert(file);
            if( result < 1 ){
                return Result.fail("文件保存到数据库失败，请重试",file);
            }
        }catch (DuplicateKeyException e){
            log.info("文件已存在，文件信息：{}", file);
            return Result.success(file);
        }
        return Result.success(file);
    }

}
